/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package client;

/**
 *
 * @author Luoaaang
 */

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;

import javax.swing.JOptionPane;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;

import common.JobObject;
import common.JobsPackage;

/**
 *
 * @author Luoaaang
 */
public class Client {

	ClientService clientService;
	// the list of jobs
	static JobsPackage jp = new JobsPackage();
	// ID for Client
	static String clientID = null;
	private int clientRevPort;
	private ArrayList<Integer> killedJobs = new ArrayList<Integer>();
	// Main Program
	public static void main(String[] args) throws JAXBException {

		// int serverIP = 127.0.0.1;
		// int serverTCPPort = 5001;

		// argument checking
		if (args.length!=2){
			System.out.println("Usage: java CloudServer <Registry IP> <Registry TCP Port>");
			return;
		}

		String serverIPStr = args[0];
		InetAddress serverIP = null;
		try {
			serverIP = InetAddress.getByName(serverIPStr);
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		int serverTCPPort = Integer.parseInt(args[1]);

		System.out.println("SYSINFO - Client has been started up successfully.");
		System.out.println("SYSINFO - Client list request service start.");
		
		// hardcoded a initialized job list for testing
//		jobList.add(new JobObject("HelloWorld", "Melbourne", 1000, 0));
//		jobList.add(new JobObject("GoodDay", "Syndey", 2000, 1));
		JobObject job1 = new JobObject();
		job1.setExecuteFilePath("clientJobs/");
		job1.setExecuteFileName("TestQueue");
		job1.setParameters("Queue1");
		job1.setTime(3000);
		job1.setJobID(3);
		jp.addJob(job1);
		JobObject job2 = new JobObject();
		job2.setExecuteFilePath("clientJobs/");
		job2.setExecuteFileName("TestQueue");
		job2.setParameters("Queue2");
		job2.setTime(4000);
		job2.setJobID(4);
		jp.addJob(job2);
		JobObject job3 = new JobObject();
		job3.setExecuteFilePath("clientJobs/");
		job3.setExecuteFileName("TestQueue");
		job3.setParameters("Queue3");
		job3.setTime(5000);
		job3.setJobID(5);
		jp.addJob(job3);

		// initialize client services
		ClientService clientService = new ClientService(serverIP, serverTCPPort, clientID);
		
		//set the deadline
		long duration = 200000;
		jp.setDeadline(duration);
		
		int budget = 100;
		jp.setBudget(budget);
		
		//New Requirements.. Jobs must be assigned by Server
		SendJobsThread sendJobsThread = new SendJobsThread(jp, clientService);
		sendJobsThread.start();

	}
	
    public void setClientID(String clientID){
        this.clientID = clientID;
    }

    public String getClientID(){
        return clientID;
    }
	
	
	public boolean GUIconnect(String serverIPStr, int serverTCPPort, int clientRevPort) {

		System.out.println(serverIPStr + ":" + serverTCPPort);
		InetAddress serverIP = null;
		try {
			serverIP = InetAddress.getByName(serverIPStr);
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
		//int serverTCPPort = Integer.parseInt(args[1]);

		System.out.println("SYSINFO - Client has been started up successfully.");
		System.out.println("SYSINFO - Client list request service start.");
		
		// initialize client services
		clientService = new ClientService(serverIP, serverTCPPort, clientID);
		clientService.setClientRevPort(clientRevPort);
		this.clientRevPort = clientRevPort;
		this.clientID = clientService.login();
		clientID = clientService.getClientID();
		//jp.setClientID(clientID);
		System.out.println("SYSINFO - clientID: "+clientService.getClientID());
		if(clientID.equals("error")){return false;}
		return true;
	}

	public void GUIIssueJob(String executeFilePath, String executeFile, String parameters, int executeTime, int jobID, int priority) {

		System.out.println("SYSINFO - Client submitted a job: " + executeFile + ";" + parameters + ";" + executeTime + ";" + jobID);
		JobObject job = new JobObject();
		job.setExecuteFilePath(executeFilePath);
		job.setExecuteFileName(executeFile);
		job.setParameters(parameters);
		job.setTime(executeTime);
		job.setJobID(jobID);
		job.setClientID(clientID);
		job.setPriority(priority);
		job.setStatus("WAITING");
		jp.addJob(job);
		String pr;
		if (priority==1) pr = "High";
		else pr = "Low";
		JOptionPane.showMessageDialog(null,"Job Added\nJob ID: " + jobID + "\nFile: "+ executeFilePath + executeFile + "\nParameters: " + parameters
				+ "\nExecution Time: " + executeTime + "\nPriority: " + pr);
	}
	
	public void QueryJobs(String aClientID,InetAddress serverAdd,int serverPort)
	{
		System.out.println("SYSINFO - Client Query Jobs Start");
		
		this.clientService = new ClientService(serverAdd, serverPort, aClientID);
		System.out.println("SYSINFO - Set the clientService Info");
		
		//clientService.setClientID(aClientID);
		jp = clientService.QueryJobsSocket(aClientID);
	}
	
	public void doQueryJobs()
	{
		System.out.println("SYSINFO - Client Query Jobs Start");
		jp = clientService.QueryJobsSocket(clientID);
	}
	
	
	public String KillJob(String aClientID,int aJobID)
	{
		System.out.println("SYSINFO - Client Kill Job Start");
		boolean kill = true;
		for (int i=0; i<killedJobs.size(); i++)
			if (killedJobs.get(i).intValue()==aJobID)
				kill = false;
		String killJobReply = "false";
		if (kill) {
			System.out.println("DEBUG - Killing Job " + aJobID);
			killJobReply = clientService.KillJobSocket(aClientID,aJobID);
			killedJobs.add(aJobID);
			
		}
		return killJobReply;
		
	}
	
	
	public void RetrieveOutput (String savePath)
	{
		System.out.println("SYSINFO - Client Retrieve Output Start");
		
		//clientService.RetrieveOutputSocket(aClientID);
		clientService.receiveResults(savePath);
	}
	
	
	public void GUIStartScheduling(String deadline, String budget, String type) {
		
		System.out.println("SYSINFO - Client Request Jobs Start");
		
		//set the deadline
		jp.setDeadline(Long.parseLong(deadline));
		
		jp.setBudget(Integer.parseInt(budget));	
		
		jp.setOptimizeType(type);
		
		SendJobsThread sendJobsThread = new SendJobsThread(jp, clientService);
		sendJobsThread.start();
		ListenNotify listenNotify = new ListenNotify(clientRevPort);
		listenNotify.start();

	}

	public void setClientRevPort(int clientRevPort) {
		clientService.setClientRevPort(clientRevPort);
	}
}

//thread for Send jobs from client to Server
class SendJobsThread extends Thread {

	private ClientService clientService;
	private JobsPackage jp = new JobsPackage();

	public SendJobsThread(JobsPackage jp, ClientService clientService) {
		this.clientService = clientService;
		this.jp = jp;
	}

	public void run() {
		// TODO Auto-generated method stub
		try {
			// wait for the worker list
			sleep(2000);
			clientService.sendJobs(jp);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}


class ListenNotify extends Thread{
	Socket clientSocket = null;
	int port;

	public ListenNotify(int port){
		this.port = port;
	}

	public void run(){
		System.out.println("SYSINFO - ListenNotify Service is start.");

		ServerSocket listenSocket;
		try {
			listenSocket = new ServerSocket(port);
			while(true)
			{
				clientSocket = listenSocket.accept();
				System.out.println("SYSINFO - Accept Master Notify TCP connection.");

				GetNotify getNotify = new GetNotify(clientSocket);
				getNotify.start();
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

class GetNotify extends Thread{        

	DataInputStream dataIn = null;
	Socket clientSocket;
	JobsPackage jobsPackage;

	public GetNotify(Socket clientSocket)
	{
		this.clientSocket = clientSocket;
	}

	public void run() {
		try{

			DataInputStream dataIn = new DataInputStream(clientSocket.getInputStream());

			String notifyXML = dataIn.readUTF();
			
			JAXBContext context = JAXBContext.newInstance(JobsPackage.class);
			Unmarshaller u = context.createUnmarshaller();
			StringBuffer xmlStr = new StringBuffer(notifyXML);
			
			jobsPackage = (JobsPackage) u.unmarshal(new StreamSource(new StringReader(xmlStr.toString())));

			if (jobsPackage.getFrom().equals("Master") && jobsPackage.getType().equals("Notify")) 
			{
				JOptionPane.showMessageDialog(null,"All Jobs Completed");
			}
		}catch (IOException e) {
			System.out.println("Listen:" + e.getMessage());
		} catch (JAXBException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			try {
				clientSocket.close();
			}catch (IOException e) {
				System.out.println("IO:" + e.getMessage());
			}
		}

	}
}






